package modularity.passiveDrift;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * The Basic Genome is just a list of connections, each referring to the two nodes being connected.
 * We maintain a list of node genes to fasten a couple of operations.
 * @author Fernando
 *
 */
public class BasicGenome implements Genome<ConnectionGene>{

	protected ArrayList<ConnectionGene> connections;
	protected ArrayList<Integer> nodes;
	private String name;
		
	public BasicGenome(){
		connections = new ArrayList<ConnectionGene>();
		nodes = new ArrayList<Integer>();
	}

	
	/**
	 * creates a basic genome with only one connection -- from "input id" to "output id"
	 * @param inputId
	 * @param outputId
	 */
	public BasicGenome(int inputId, int outputId){
		super();
		connections.add(new ConnectionGene(inputId, outputId));
	}
	
	//creating the initial genome given the ids of the genes
	public BasicGenome(ArrayList<Integer> fromNodesIds, ArrayList<Integer> toNodesIds){
		connections = new ArrayList<ConnectionGene>(fromNodesIds.size() * toNodesIds.size());
		nodes = new ArrayList<Integer>(fromNodesIds.size() + toNodesIds.size());
		nodes.addAll(fromNodesIds);
		nodes.addAll(toNodesIds);
		
		for(int from : fromNodesIds){
			for(int to: toNodesIds){
				connections.add(new ConnectionGene(from, to));
			}
		}
	}

	public int getNumberOfConnections() {
		return connections.size();
	}

	public void removeConnection(int index) {
		ConnectionGene toRemove = connections.remove(index);
		
		//update the list of nodes.
		boolean foundFrom = false, foundTo = false;
		for(int i = 0; i < connections.size() && (!foundFrom || !foundTo); i++){
			ConnectionGene connection = connections.get(i);
			if(connection.containsNodeGene(toRemove.getFromGene()))
				foundFrom = true;
			
			if(connection.containsNodeGene(toRemove.getToGene()))
				foundTo = true;
		}
		
		//the "from" node in the connection to remove is not in any other connection so it is safe to remove it from the list of nodes
		if(!foundFrom){
			int pos = nodes.indexOf(toRemove.getFromGene());
			nodes.remove(pos);
		}
		if(!foundTo){
			int pos = nodes.indexOf(toRemove.getToGene());
			nodes.remove(pos);
		}
	}

	public boolean containsConnection(ConnectionGene newConnection) {
		//contains in an arraylist is based on the "equals" method.
		return connections.contains(newConnection);
	}

	public void addConnection(ConnectionGene newConnection) {
		connections.add(newConnection);
		
		if(!nodes.contains(newConnection.getFromGene()))
			nodes.add(newConnection.getFromGene());
		
		if(!nodes.contains(newConnection.getToGene()))
			nodes.add(newConnection.getToGene());
	}

	public ConnectionGene getConnectionAtIndex(int index) {
		return connections.get(index);
	}
	
	public ArrayList<Integer> getListOfNodes(){
		return nodes;
	}

	public int getNumberOfNodes() {
		return nodes.size();
	}

	public int getNodeAt(int index) {
		return nodes.get(index);
	}
	
	@Override
	public ArrayList<ConnectionGene> getGenomicComponents() {
		return this.connections;
	}
	
	@Override
	public String getName() {
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public String toString(){
		StringBuilder builder = new StringBuilder();
		for(ConnectionGene gene : this.connections){
			builder.append(gene.getFromGene());
			builder.append(" ");
			builder.append(gene.getToGene());
			builder.append("\n");
		}
		return builder.toString();
	}


	public LinkedList<ConnectionGene> getConnectionsWithNode(int nodeId) {
		LinkedList<ConnectionGene> result = new LinkedList<ConnectionGene>();
		for(ConnectionGene connection : this.connections){
			if(connection.containsNodeGene(nodeId))
				result.add(connection);
		}
		return result;
	}
	
}
